package kiang.teb;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Copyright (c) 2014 by kiang
 *
 * @author kiang
 * @version 0.1-pre
 */
public final class Performer implements Runnable {
    private Map data;
    private File file; 
    private int looped;
    private String encoding;
    private TebEngine engine;
    private boolean binary;

    @Override
    public void run() {
        try {
            long launch, finish;
            TebEngine engine = this.engine;
            Map data = this.data;
            if (this.binary) {
                if (engine.isBinarySupport()) {
                    ByteStream loopStream = new ByteStream();
                    launch = System.currentTimeMillis();
                    for (int i = 0, n = this.looped; i < n; i++) {
                        engine.test(data, loopStream);
                    }
                    loopStream.flush();
                    finish = System.currentTimeMillis();
                    loopStream.close();
                
                } else {
                    LinkStream loopStream = new LinkStream(this.encoding);
                    launch = System.currentTimeMillis();
                    for (int i = 0, n = this.looped; i < n; i++) {
                        engine.test(data, loopStream);
                    }
                    loopStream.flush();
                    finish = System.currentTimeMillis();
                    loopStream.close();
                }
            } else {
                CharStream loopStream = new CharStream(this.encoding);
                launch = System.currentTimeMillis();
                for (int i = 0, n = this.looped; i < n; i++) {
                    engine.test(data, loopStream);
                }
                loopStream.flush();
                finish = System.currentTimeMillis();
                loopStream.close();
            
            }
            TebUtilities.println(this.file, "bot:" + launch, "eot:" + finish);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String args[]) throws Exception {
        test(new String[]{
                System.getenv("binary"),
                System.getenv("thread"),
                System.getenv("record"),
                System.getenv("warned"),
                System.getenv("looped"),
                System.getenv("locate"),
                System.getenv("source"),
                System.getenv("target"),
                System.getenv("tester"),
                System.getenv("simple")
        });
    }

    public static void test(String args[]) throws Exception {
        boolean binary = Boolean.parseBoolean(args[0]);
        int thread = Integer.parseInt(args[1]);
        int record = Integer.parseInt(args[2]);
        int warmed = Integer.parseInt(args[3]);
        int looped = Integer.parseInt(args[4]);
        String locate = args[5];
        String source = args[6];
        String target = args[7];
        String tester = args[8];
        String simple = args[9];

        Properties properties = new Properties();
        properties.setProperty("source", source);
        properties.setProperty("target", target);
        properties.setProperty("binary", String.valueOf(binary));

        Map data = new HashMap();
        data.put("target", target);
        data.put("models", TebModel.dummyModels(record));


        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        TebEngine engine = (TebEngine) classLoader.loadClass(simple).newInstance();

        engine.init(properties);

        long once;
        File outputFile = new   File(locate, tester + ".html");
        if (binary) {
            if (engine.isBinarySupport()) {
                ByteStream warmStream = new ByteStream();
                for (int i = 0; i < warmed; i++) {
                    engine.test(data, warmStream);
                }
                warmStream.flush();
                warmStream.close();
                once = warmStream.size() / warmed;
                
                FileOutputStream fos = new FileOutputStream(outputFile);
                engine.test(data, fos);
                fos.close();
                
            } else {
                LinkStream warmStream = new LinkStream(target);
                for (int i = 0; i < warmed; i++) {
                    engine.test(data, warmStream);
                }
                warmStream.flush();
                warmStream.close();
                once = warmStream.size() / warmed;
            }
        } else {
            CharStream warmStream = new CharStream(target);
            for (int i = 0; i < warmed; i++) {
                engine.test(data, warmStream);
            }
            warmStream.flush();
            warmStream.close();
            once = warmStream.size() / warmed;
            FileWriter fw = new FileWriter(outputFile);
            engine.test(data, fw);
            fw.close();
        }
        long mass = once * looped * thread;
        File file = new File(locate, tester + ".txt");
     
        TebUtilities.println(file, "obs:" + once, "mbs:" + mass);

        Performer performer;
        if (thread == 1) {
            performer = new Performer();
            performer.data = data;
            performer.file = file;
            performer.looped = looped;
            performer.binary = binary;
            performer.engine = engine;
            performer.encoding = target;
        
            performer.run();
            engine.shut();         
            return;
        }
        ExecutorService threadPool = Executors.newFixedThreadPool(Math.min(thread, 200));
        for (int i = 0; i < thread; i++) {
            performer = new Performer();
            performer.data = data;
            performer.file = file;
            performer.looped = looped;
            performer.binary = binary;
            performer.engine = engine;
            performer.encoding = target;
         
            threadPool.execute(performer);
        }
        threadPool.shutdown();
        
        
        // wait thread pool finish.
        while (!threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS)) ;
        engine.shut();
    }
}
